#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <time.h>
#include <signal.h>
#include "proto.h"

#define MINSPARESERVER 5
#define MAXSPARESERVER 10
#define MAXCLIENT 20

// define signal
#define SIG_NOTIFY SIGUSR2

enum {
	STATE_IDLE = 0,
	STATE_BUSY	
};

struct server_st {
	pid_t pid;
	int state;
};

static struct server_st *serverpool;
static int idle_count = 0;
static int busy_count = 0;

usr2_handler() {

}

void server_job(int pos) {
	pid_t ppid = getppid();
	struct sockaddr_in raddr;
	socklen_t raddr_len;
	int client_sd;

	while (1) {
		server_pool[pos].state = STATE_IDLE;
		kill(ppid, SIG_NOTIFY);
		client_sd = accept(sd, (void*)&raddr, &raddr_len);
		if (client_sd < 0) {
			if (errno != EINTR || errno != EAGAIN) {
				perror("accept()");
				exit(1);
			}
		}
		serverpool[pos].state = STATE_BUSY;
		kill(ppid, SIG_NOTIFY);
		inet_ntop(AF_INET, &raddr.sin_addr, ipstr, IPSTRSIZE);
		printf("[%d]client:%s:%d\n", getpid(), ipstr, ntohs(raddr.sin_port));
		time_t stamp = time(NULL);
		int len = snprintf(linebuf, LINEBUFSIZE, FMT_STAMP, stamp);
		send(client_sd, linebuf, len, 0);
		/*if error*/
		sleep(5);
		close(client_sd);
	}
}

static int add_1_server(void) {
	if (idle_count + busy_count >= MAXCLIENTS) {
		return -1;
	}
	int slot;
	for (slot = 0; slot < MAXCLIENT; ++slot) {
		if (serverpool[slot].pid == -1) {
			break;
		}
	}
	serverpool[slot].state = STATE_IDLE;
	int pid = fork();
	if (pid < 0) {
		perror("fork()");	
		exit(1);
	}
	if (pid == 0) {
		// child
		server_job(slot);
		exit(0);
	}
	else {
		// parent
		serverpool[slot].pid = pid;
		serverpool[slot].state = STATE_IDLE;
		idle_count++;
	}
	return 0;
}

static int del_1_server(void) {
	if (idle_count == 0)
		return -1;
	for (int i = 0; i < MAXCLIENTS; ++i) {
		if (serverpool[i].pid != -1 && serverpool[i].state == STATE_IDLE) {
			kill(serverpool[i].pid, SIGTERM);
			serverpool[i].pid = -1;
			--idle_count;
			break;
		}	
	}
	return 0;
}

static int scan_pool(void) {
	int busy = 0, idle = 0;
	for (int i = 0; i < MAXCLIENTS; ++i) {
		if (serverpool[i].pid == -1) {
			continue;
		}
		if (kill(serverpool[i].pid, 0)) {
			serverpool[i].pid = -1;
			continue;
		}
		if (serverpool[i].state == STATE_IDLE) {
			++idle;
		}
		else if (serverpool[i].state == STATE_BUSY) {
			++busy;
		}
		else {
			fprintf(stderr, "Unknown state\n");
			//_exit(1);
			abort();
		}
	}
	idle_count = idle;
	busy_count = busy;
}



int main()
{
	struct sigaction sa, osa;
	sa.sa_handler = SIG_IGN;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_NOCLDWAIT;
	sigaction(SIGCHLD, &sa, &osa);

	sa.sa_handler = usr2_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sigaction(SIG_NOTIFY, &sa, &osa);

	sigemptyset(&set);
	sigaddset(&set, SIG_NOTIFY);
	sigprocmask(SIG_BLOCK, &set, &osa);

	// apply for memory space
	serverpool = mmap(NULL, sizeof(struct server_st) * MAXCLIENTS, PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
	if (serverpool == MAP_FAILED) {
		perror("mmap()");
		exit(1);
	}

	int sd = socket(AF_INET, SOCK_STREAM, 0);
	if (sd < 0) {
		perror("socket()");
		exit(1);
	}
	
	int val = 1;
	if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
		perror("setsockopt");
		exit(1);
	}

	struct sockaddr_in laddr;
	laddr.sin_family = AF_INET;
	laddr.sin_port = htons(atoi(SERVERPORT));
	inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);
	if (bind(sd, (void*)&laddr, sizeof(laddr)) < 0) {
		perror("bind()");
		exit(1);
	}

	// set socket mode to listen
	if (listen(sd, 100) < 0) {
		perror("listen");	
		exit(1);
	}

	for (int i = 0; i < MINSPARESERVER; ++i) {
		add_1_server();
	}

	while () {
		sigsuspend();
		scan_pool();
		// control the pool
		if (idle_count > MAXSPARESERVER) {
			for (int i = 0; i < (idle_count-MAXSPARESERVER); ++i) {
						del_1_server();
					}
		}
		else if (idle_count < MAXSPARESERVER){
			for (int i = 0; i < (MAXSPARESERVER - idle_count); ++i) {
                                                add_1_server();
                                        }
		}

		// printf
		for (int i = 0; i < MAXCLIENTS; ++i) {
			
		}
		
	}
	sigprockmask(SIG_SETMASK, &oset, NULL);

	exit(0);
}
